package gao.xiaolei.service;

import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import javax.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import gao.xiaolei.dao.TestProjectDao;
import gao.xiaolei.dao.TestRequirementCheckDao;
import gao.xiaolei.dao.TestRequirementDao;
import gao.xiaolei.dto.TestCheckCreateDto;
import gao.xiaolei.dto.TestCheckDetailDto;
import gao.xiaolei.dto.TestCheckDto;
import gao.xiaolei.dto.TestCheckLogDto;
import gao.xiaolei.entity.TestProject;
import gao.xiaolei.entity.TestRequirement;
import gao.xiaolei.entity.TestRequirementCheck;
import xiaolei.gao.file.FileInterface;
import gao.xiaolei.util.ServiceName;
import gao.xiaolei.util.UuidUtil;

@Service("TestRequirementCheckService")
public class TestRequirementCheckService {

    @Resource(name = "TestRequirementCheckDao")
    private TestRequirementCheckDao testRequirementCheckDao;

    @Resource(name = "TestRequirementDao")
    private TestRequirementDao testRequirementDao;

    @Resource(name = "TestProjectDao")
    private TestProjectDao testProjectDao;

    @Autowired
    private RestTemplate restTemplate;

    @Resource(name = "FileInterface")
    private FileInterface fileInterface;

    @Resource(name = "AnalysisService")
    private AnalysisService analysisService;

    //添加一个审核记录
    @Transactional
    public void addCheck(List<TestCheckCreateDto> testRequirementChecks) {
        Map<String, List<TestCheckCreateDto>> checkMap = new HashMap<>();//将要添加的测试需求进行分组
        for (int i = 0, length = testRequirementChecks.size(); i < length; i++) {
            String thisTreqRoot = getTestRequirementRootParent(testRequirementChecks.get(i).getTargetId());
            if (!checkMap.containsKey(thisTreqRoot))
                checkMap.put(thisTreqRoot, new LinkedList<TestCheckCreateDto>());
            checkMap.get(thisTreqRoot).add(testRequirementChecks.get(i));
        }
        for (Map.Entry<String, List<TestCheckCreateDto>> s : checkMap.entrySet()) {
            List<TestCheckCreateDto> ts = s.getValue();
            Integer count = recurrenceAllCount(s.getKey()) + 1;
            boolean isSelectAll = count == s.getValue().size();
            recurrenceCreateCheckTree(s.getKey(), "root", s.getValue(), isSelectAll);//根据顶端测试需求递归生成相同树形结构的测试需求审核记录
            analysisService.doAnalysis(testRequirementDao.getProject(s.getKey()));//重新生成测试需求分析
        }
    }

    //获取某个审核人已经审核完的记录,但只是返回最顶端节点
    @Transactional(readOnly = true)
    public List<TestCheckDto> findByCheckManAndFinshed(String checkMan, String projectId) {
        List<TestRequirementCheck> trc = objToTestRequirement6(testRequirementCheckDao.findByCheckManAndIsFinishAndParentIdAndProjectId(checkMan, 1, "root", projectId));
        List<TestCheckDto> trcDtos = new ArrayList<>(trc.size());
        for (int i = 0, length = trc.size(); i < length; i++) {
            String targetName = Optional.ofNullable(testRequirementDao.getTitle(trc.get(i).getTargetId())).orElse("已删除");
            String result = trc.get(i).getIsPass() == 1 ? "审核通过" : "审核未通过";
            trcDtos.add(new TestCheckDto(trc.get(i).getId(), targetName, result, trc.get(i).getCheckDate(), trc.get(i).getParentId(), trc.get(i).getLeaf()));
        }
        return trcDtos;
    }

    //获取某个审核人已经开始审核但是未审核完的记录,但只是返回最顶端节点
    @Transactional(readOnly = true)
    public List<TestCheckDto> findByCheckManAndNotFinshed(String checkMan, String projectId) {
        List<TestRequirementCheck> trc = objToTestRequirement6(testRequirementCheckDao.findByCheckManAndIsFinishAndParentIdAndProjectId(checkMan, 0, "root", projectId));
        return getCheckDtoList(trc);
    }

    //获取某个审核记录的子节点
    @Transactional(readOnly = true)
    public List<TestCheckDto> getTestCheckChildren(String id) {
        List<TestRequirementCheck> trc = testRequirementCheckDao.findByParentId(id);
        return getCheckDtoList(trc);
    }


    //获取某个测试需求的的所有审核记录,但只是返回id,isPass,checkMan,checkDate,isFinish
    @Transactional(readOnly = true)
    public List<TestCheckLogDto> findByTestRequirement(String testRequirement) {
        List<TestRequirementCheck> checkLogs = objToTestRequirement5(testRequirementCheckDao.findByTarget(testRequirement));
        List<TestCheckLogDto> dtos = new ArrayList<>(checkLogs.size());
        for (int i = 0, length = checkLogs.size(); i < length; i++) {
            String checkManName = restTemplate.getForObject(ServiceName.AUTH_SERVICE.getServiceName().concat("/user/realName/").concat(checkLogs.get(i).getCheckMan()), String.class);
            dtos.add(new TestCheckLogDto(checkLogs.get(i).getId(), checkManName, checkLogs.get(i).getIsPass(), checkLogs.get(i).getIsFinish(), checkLogs.get(i).getCheckDate()));
        }
        return dtos;
    }

    //获取某个测试需求的的所有审核记录的条数
    public Integer getConutByTestRequirement(String testRequirement) {
        return testRequirementCheckDao.getCountByTargetId(testRequirement);
    }

    //获取审核的批注内容
    public String getAnnotate(String id) {
        return Optional.ofNullable(testRequirementCheckDao.getAnnotate(id)).orElse("无");
    }

    //修改审核是否通过和审核批注
    @Transactional
    public void updateAnnotateAndIsPass(List<TestCheckCreateDto> chooseCheck, Integer isFinish) {
        for (int i = 0, length = chooseCheck.size(); i < length; i++) {//先将要更新的审核全部更新掉
            testRequirementCheckDao.updateAnnotateAndIsPass(chooseCheck.get(i).getAnnotate(), chooseCheck.get(i).getIsPass(), chooseCheck.get(i).getId());
        }
        if (isFinish == 0) {//如果是在未完成的的面版选择修改的话
            Set<String> checkSet = new HashSet<>();//将要修改的测试审核进行分组
            for (int i = 0, length = chooseCheck.size(); i < length; i++) {
                String thisCheckRoot = getTestCheckRootParent(chooseCheck.get(i).getId());
                checkSet.add(thisCheckRoot);
            }
            for (String s : checkSet) {
                String targetId = testRequirementCheckDao.getTarget(s);
                if (!recurrenceIsPass(s, -1)) {//如果在这次修改之后全部审核都给出了结果的话
                    recurrenceUpdateIsFinishTo1(s);//就将全部审核改为已完成，并且将对应点测试需求状态也一并修改了
                    if (recurrenceIsPass(s, 0))//如果整棵树的审核结果并不是全部都通过了的话
                        testRequirementDao.updateStatus(targetId, -1);//就要将最顶端的测试需求的状态改为审核不通过
                }
                analysisService.doAnalysis(testRequirementDao.getProject(targetId));//重新生成测试需求分析
            }
        } else {//如果是在已完成的面板选择修改的话
            Map<String, List<TestCheckCreateDto>> checkMap = new HashMap<>();//将要修改的测试审核进行分组
            for (int i = 0, length = chooseCheck.size(); i < length; i++) {
                String thisCheckRoot = getTestCheckRootParent(chooseCheck.get(i).getId());
                if (!checkMap.containsKey(thisCheckRoot))
                    checkMap.put(thisCheckRoot, new LinkedList<TestCheckCreateDto>());
                checkMap.get(thisCheckRoot).add(chooseCheck.get(i));
            }
            for (Map.Entry<String, List<TestCheckCreateDto>> s : checkMap.entrySet()) {
                String targetId = testRequirementCheckDao.getTarget(s.getKey());
                int count = testRequirementCheckDao.getCountByTargetIdAndCheckDateAfter(targetId, testRequirementCheckDao.getCheckDate(s.getKey()));
                if (count == 0) {//如果这个审核是最新的审核的话，修改掉它的结果就可以影响到测试需求本身
                    for (TestCheckCreateDto t : s.getValue()) {//将修改了的审核对应的测试需求对应的状态改掉
                        testRequirementDao.updateStatus(targetId, t.getIsPass() == 1 ? 3 : -1);
                    }
                    if (recurrenceIsPass(s.getKey(), 0))//如果整棵树的审核结果并不是全部都通过了的话
                        testRequirementDao.updateStatus(targetId, -1);//就要将最顶端的测试需求的状态改为审核不通过
                    else
                        testRequirementDao.updateStatus(targetId, 3);//就要将最顶端的测试需求的状态改为审核通过
                    analysisService.doAnalysis(testRequirementDao.getProject(targetId));//重新生成测试需求分析
                }
            }
        }
    }

    //获取审核详情
    @Transactional(readOnly = true)
    public TestCheckDetailDto getDetail(String id, Integer status) {//如果是待审核的话，传过来的id就是指对应的测试需求Id
        String testReqId = null, projectId = null;
        TestRequirementCheck t = null;
        if (status != -1) {//如果不是在待审核界面查看
            t = testRequirementCheckDao.findById(id).get();
            testReqId = t.getTargetId();
            projectId = t.getProjectId();
        } else {
            testReqId = id;
            projectId = testRequirementDao.getProject(id);
        }
        String targetName = Optional.ofNullable(testRequirementDao.getTitle(testReqId)).orElse("已删除");
        Integer isDeleted = 0;
        String targetDescribe = null;
        if ("已删除".equals(targetName)) {//说明这个测试需求已经被删除了
            isDeleted = 1;
            targetDescribe = "该测试需求已经删除";
        } else {
            byte[] fileByte = fileInterface.getFile(testReqId, projectId);
            targetDescribe = new String(fileByte, StandardCharsets.UTF_8);
        }
        if (status != -1)//如果不是在待审核界面查看
            return new TestCheckDetailDto(id, testReqId, targetName, t.getIsPass(), t.getAnnotate(), t.getCheckDate(), isDeleted, targetDescribe);
        else
            return new TestCheckDetailDto(null, testReqId, targetName, 0, "", null, isDeleted, targetDescribe);
    }

    //获取某个项目待审核的测试需求(树形版,只先获取最顶端的测试需求)
    @Transactional(readOnly = true)
    public List<TestCheckDto> getReadyToCheck(String projectId) {
        List<TestRequirement> trqs = testRequirementDao.findByProjectAndStatusAndParentId(projectId, 1, "root");
        List<TestCheckDto> testCheckDtos = new ArrayList<>(trqs.size());
        for (int i = 0, length = trqs.size(); i < length; i++) {
            testCheckDtos.add(new TestCheckDto(null, trqs.get(i).getTitle(), trqs.get(i).getId(), null, null, trqs.get(i).getParentId(), trqs.get(i).getLeaf()));
        }
        return testCheckDtos;
    }

    //获取某个测试需求节点下的待审核的测试需求
    @Transactional(readOnly = true)
    public List<TestCheckDto> getTestRequirementChildren(String id) {
        List<TestCheckDto> testCheckDtos = new ArrayList<>();
        List<TestRequirement> tqs = testRequirementDao.findByStatusAndParentId(1, id);
        for (int i = 0, length = tqs.size(); i < length; i++) {
            testCheckDtos.add(new TestCheckDto(tqs.get(i).getId(), tqs.get(i).getTitle(), tqs.get(i).getId(), null, null, tqs.get(i).getParentId(), tqs.get(i).getLeaf()));
        }
        return testCheckDtos;
    }

    //删除一个审核记录
    @Transactional
    public void deleteCheck(String id) {
        testRequirementCheckDao.deleteById(id);
    }

    //**********************************树操作begin**************************************************//

    //根据顶端测试需求递归生成相同树形结构的测试需求审核记录（测试需求Id,本次递归的审核记录的父Id,明确选择的审核对象,选择项是否选择完全）
    private void recurrenceCreateCheckTree(String testRequirementId, String checkParentId, List<TestCheckCreateDto> chooseCheckList, boolean selectAll) {
        boolean flag = false;
        Integer isLeaf = testRequirementDao.getLeaf(testRequirementId);
        TestCheckCreateDto chooseCheck = null;
        for (TestCheckCreateDto t : chooseCheckList) {//判断这次递归的测试需求是否是用户填写了审核内容的测试需求
            if (testRequirementId.equals(t.getTargetId())) {
                chooseCheck = t;
                flag = true;
                break;
            }
        }
        String thisCheckId = UuidUtil.get32Uuid();//生成审核记录Id,然后向下递归时就以此为下一层的父审核Id
        if (selectAll)//如果用户全部选中的话，代表直接完成此次审核，可以直接将结果赋给测试需求本身
            testRequirementDao.updateStatus(testRequirementId, chooseCheckList.get(0).getIsPass() == 1 ? 3 : -1);
        else//如果没有全部选中，代表没有完成此次审核，所以要将测试需求本身设置为审核中
            testRequirementDao.updateStatus(testRequirementId, 2);
        if (flag) //如果用户填写了此审核的话就用用户填写的内容来生成审核
            testRequirementCheckDao.save(new TestRequirementCheck(thisCheckId, testRequirementId, chooseCheckList.get(0).getCheckMan(), chooseCheck.getAnnotate(), chooseCheck.getIsPass(), selectAll ? 1 : 0, LocalDateTime.now(Clock.system(ZoneId.of("Asia/Shanghai"))), checkParentId, isLeaf, testRequirementDao.getProject(testRequirementId)));
        else//不然就用默认的内容生成(未选择的话isPass就是-1)
            testRequirementCheckDao.save(new TestRequirementCheck(thisCheckId, testRequirementId, chooseCheckList.get(0).getCheckMan(), " ", -1, selectAll ? 1 : 0, LocalDateTime.now(Clock.system(ZoneId.of("Asia/Shanghai"))), checkParentId, isLeaf, testRequirementDao.getProject(testRequirementId)));
        if (isLeaf != 1) {//如果不是叶子节点就继续递归,生成审核记录
            List<String> cIds = testRequirementDao.getChildrenId(testRequirementId);//获取子需求
            for (int i = 0, length = cIds.size(); i < length; i++) {
                recurrenceCreateCheckTree(cIds.get(i), thisCheckId, chooseCheckList, selectAll);
            }
        }
    }

    //根据顶端测试需求递归查出总共有多少个节点
    private Integer recurrenceAllCount(String id) {
        int sum = 0;
        Integer isLeaf = testRequirementDao.getLeaf(id);
        if (isLeaf == 0) {//如果有儿子就向下递归儿子查看每个儿子下面有多少个节点
            List<String> children = testRequirementDao.getChildrenId(id);
            for (int i = 0, length = children.size(); i < length; i++) {
                sum = sum + recurrenceAllCount(children.get(i));
            }
            sum = sum + children.size();
        }
        return sum;//向上传递自己的儿子数量
    }

    //递归测试审核树，将整棵树的isFinish修改为1(即审核完成)
    private void recurrenceUpdateIsFinishTo1(String id) {
        testRequirementCheckDao.updateIsFinish(1, id);
        String treqId = testRequirementCheckDao.getTarget(id);
        testRequirementDao.updateStatus(treqId, testRequirementCheckDao.getIsPass(id) == 1 ? 3 : -1);//将审核的测试需求根据审核结果修改状态
        if (testRequirementCheckDao.getLeaf(id) == 0) {
            List<String> children = testRequirementCheckDao.getChildren(id);
            for (int i = 0, length = children.size(); i < length; i++) {
                recurrenceUpdateIsFinishTo1(children.get(i));
            }
        }
    }

    //递归测试审核树，查看是否整棵树的isPass是否有等于某个值的
    private boolean recurrenceIsPass(String id, Integer require) {
        Integer isPass = testRequirementCheckDao.getIsPass(id);
        if (require.equals(isPass))
            return true;
        else {
            boolean flag = false;
            if (testRequirementCheckDao.getLeaf(id) == 0) {
                List<String> children = testRequirementCheckDao.getChildren(id);
                for (int i = 0, length = children.size(); i < length; i++) {
                    flag = recurrenceIsPass(children.get(i), require);
                    if (flag)
                        break;
                }
            }
            return flag;
        }
    }

    //获取该测试需求节点的最顶端节点
    private String getTestRequirementRootParent(String id) {
        String result = id;
        String parentId = testRequirementDao.getParentId(id);
        while (!"root".equals(parentId) && parentId != null) {
            result = parentId;
            parentId = testRequirementDao.getParentId(parentId);
        }
        return result;
    }

    //获取该测试审核节点的最顶端节点
    private String getTestCheckRootParent(String id) {
        String result = id;
        String parentId = testRequirementCheckDao.getParentId(id);
        while (!"root".equals(parentId) && parentId != null) {
            result = parentId;
            parentId = testRequirementCheckDao.getParentId(parentId);
        }
        return result;
    }
    //**********************************树操作end**************************************************//

    //**********************************数据库字段转换begin**************************************************//

    //用于将从数据库里查出来的List<Object[]>转换为List<TestRequirementCheck>(id,isPass,checkMan,isFinish,checkDate)
    private List<TestRequirementCheck> objToTestRequirement5(List<Object[]> os) {
        List<TestRequirementCheck> ts = new ArrayList<>(os.size());
        for (int i = 0, length = os.size(); i < length; i++) {
            ts.add(new TestRequirementCheck((String) os.get(i)[0], (Integer) os.get(i)[1], (String) os.get(i)[2], (Integer) os.get(i)[3], (LocalDateTime) os.get(i)[4]));
        }
        return ts;
    }

    // 用于将从数据库里查出来的List<Object[]>转换为List<TestRequirementCheck>(id,requirementId,isPass,checkDate,leaf,parentId)
    private List<TestRequirementCheck> objToTestRequirement6(List<Object[]> os) {
        List<TestRequirementCheck> ts = new ArrayList<>(os.size());
        for (int i = 0, length = os.size(); i < length; i++) {
            ts.add(new TestRequirementCheck((String) os.get(i)[0], (String) os
                    .get(i)[1], (Integer) os.get(i)[2], (LocalDateTime) os.get(i)[3], (Integer) os.get(i)[4], (String) os.get(i)[5]));
        }
        return ts;
    }

    // 用于将从数据库里查出来的List<Object[]>转换为List<TestProject>(id,name)
    private List<TestProject> objToTestProject2(List<Object[]> os) {
        List<TestProject> ts = new ArrayList<>(os.size());
        for (int i = 0, length = os.size(); i < length; i++) {
            ts.add(new TestProject((String) os.get(i)[0], (String) os.get(i)[1]));
        }
        return ts;
    }

    //将数据库查出来的List<TestRequirementCheck>转换成List<TestCheckDto>
    private List<TestCheckDto> getCheckDtoList(List<TestRequirementCheck> trc) {
        List<TestCheckDto> trcDtos = new ArrayList<>(trc.size());
        for (int i = 0, length = trc.size(); i < length; i++) {
            String targetName = Optional.ofNullable(testRequirementDao.getTitle(trc.get(i).getTargetId())).orElse("已删除");
            String result = null;
            switch (trc.get(i).getIsPass()) {
                case -1:
                    result = "未选择";
                    break;
                case 0:
                    result = "审核未通过";
                    break;
                case 1:
                    result = "审核通过";
                    break;
            }
            trcDtos.add(new TestCheckDto(trc.get(i).getId(), targetName, result, trc.get(i).getCheckDate(), trc.get(i).getParentId(), trc.get(i).getLeaf()));
        }
        return trcDtos;
    }

    //**********************************数据库字段转换end**************************************************//
}
